#include "defines.h"
#include "kozos.h"
#include "syscall.h"
#include "interrupt.h"
#include "kernel.h"

// システムコール呼出関数。in_thread == 0は割込ハンドラから呼ばれた場合。

kz_thread_id_t kz_run(kz_func_t func, char *name, int priority, unsigned stacksize, int argc, char *argv[])
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.run.func = func;
        param.un.run.name = name;
        param.un.run.priority = priority;
        param.un.run.stacksize = stacksize;
        param.un.run.argc = argc;
        param.un.run.argv = argv;
        ky_syscall(KZ_SYSCALL_TYPE_RUN, &param);
        return param.un.run.ret;
    } else {
        return sys_run(func, name, priority, stacksize, argc, argv);
    }
}

void kz_exit(void)
{
    if (in_thread) {
        ky_syscall(KZ_SYSCALL_TYPE_EXIT, NULL);
    } else {
        ASSERT(0, "kz_exit: cannot call in interrupt");
    }
}

int kz_wait(uint32 tick)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.wait.tick = tick;
        ky_syscall(KZ_SYSCALL_TYPE_WAIT, &param);
        return param.un.wait.ret;
    } else {
        ASSERT(0, "kz_wait: cannot call in interrupt");
        return 0;
    }
}

int kz_sleep(void)
{
    if (in_thread) {
        kz_syscall_param_t param;
        ky_syscall(KZ_SYSCALL_TYPE_SLEEP, &param);
        return param.un.sleep.ret;
    } else {
        ASSERT(0, "kz_sleep: cannot call in interrupt");
        return 0;
    }
}

int kz_wakeup(kz_thread_id_t id)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.wakeup.id = id;
        ky_syscall(KZ_SYSCALL_TYPE_WAKEUP, &param);
        return param.un.wakeup.ret;
    } else {
        return sys_wakeup(id);
    }
}

kz_thread_id_t kz_getid(void)
{
    if (in_thread) {
        kz_syscall_param_t param;
        ky_syscall(KZ_SYSCALL_TYPE_GETID, &param);
        return param.un.getid.ret;
    } else {
        ASSERT(0, "kz_getid: cannot call in interrupt");
        return 0;
    }
}

int kz_chpri(int priority)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.chpri.priority = priority;
        ky_syscall(KZ_SYSCALL_TYPE_CHPRI, &param);
        return param.un.chpri.ret;
    } else {
        ASSERT(0, "kz_chpri: cannot call in interrupt");
        return 0;
    }
}

void *kz_kmalloc(int size)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.kmalloc.size = size;
        ky_syscall(KZ_SYSCALL_TYPE_KMALLOC, &param);
        return param.un.kmalloc.ret;
    } else {
        return sys_kmalloc(size);
    }
}

int kz_kmfree(void *p)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.kmfree.p = p;
        ky_syscall(KZ_SYSCALL_TYPE_KMFREE, &param);
        return param.un.kmfree.ret;
    } else {
        return sys_kmfree(p);
    }
}

int kz_send(kz_msgbox_id_t id, int size, char *p, int priority)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.send.id = id;
        param.un.send.size = size;
        param.un.send.p = p;
        param.un.send.priority = priority;
        ky_syscall(KZ_SYSCALL_TYPE_SEND, &param);
        return param.un.send.ret;
    } else {
        kz_thread *tp = current;
        int ret;
        current = NULL; /* 送り主スレッドはない */
        ret = sys_send(id, size, p, priority);
        current = tp;
        return ret;
    }
}

kz_thread_id_t kz_recv(kz_msgbox_id_t id, int *sizep, char **pp)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.recv.id = id;
        param.un.recv.sizep = sizep;
        param.un.recv.pp = pp;
        ky_syscall(KZ_SYSCALL_TYPE_RECV, &param);
        return param.un.recv.ret;
    } else {
        ASSERT(0, "kz_recv: cannot call in interrupt");
        return 0;
    }
}

kz_thread_id_t kz_recva(kz_msgbox_id_t id, int *sizep, char **pp, uint32 tick)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.recva.id = id;
        param.un.recva.sizep = sizep;
        param.un.recva.pp = pp;
        param.un.recva.tick = tick;
        ky_syscall(KZ_SYSCALL_TYPE_RECVA, &param);
        return param.un.recva.ret;
    } else {
        ASSERT(0, "kz_recva: cannot call in interrupt");
        return 0;
    }
}

int kz_setintr(softvec_type_t type, kz_handler_t handler)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.setintr.type = type;
        param.un.setintr.handler = handler;
        ky_syscall(KZ_SYSCALL_TYPE_SETINTR, &param);
        return param.un.setintr.ret;
    } else {
        return sys_setintr(type, handler);
    }
}

kz_thread_info_t *kz_getthreads(int *sizep)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.getthreads.sizep = sizep;
        ky_syscall(KZ_SYSCALL_TYPE_GETTHREADS, &param);
        return param.un.getthreads.ret;
    } else {
        return sys_getthreads(sizep);
    }
}

kz_msgbox_id_t kz_getmsgbox()
{
    if (in_thread) {
        kz_syscall_param_t param;
        ky_syscall(KZ_SYSCALL_TYPE_GETMSGBOX, &param);
        return param.un.getmsgbox.ret;
    } else {
        return sys_getmsgbox();
    }
}

int kz_freemsgbox(kz_msgbox_id_t id)
{
    if (in_thread) {
        kz_syscall_param_t param;
        param.un.freemsgbox.id = id;
        ky_syscall(KZ_SYSCALL_TYPE_FREEMSGBOX, &param);
        return param.un.freemsgbox.ret;
    } else {
        return sys_freemsgbox(id);
    }
}

int kz_resource()
{
    if (in_thread) {
        kz_syscall_param_t param;
        ky_syscall(KZ_SYSCALL_TYPE_RESOURCE, &param);
        return param.un.resource.ret;
    } else {
        return sys_resource();
    }
}

uint32 kz_systime()
{
    if (in_thread) {
        kz_syscall_param_t param;
        ky_syscall(KZ_SYSCALL_TYPE_SYSTIME, &param);
        return param.un.systime.ret;
    } else {
        return sys_systime();
    }
}
